﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using Infrastructure.DataTypes;

namespace Infrastructure
{
    public class Item<R> where R : ICountable, new()//Data set now only for Icountable 
    {
        private string itemID;//item id , that's how the item identified in data set
        public StatisticAttributes StatisticsAttributes { get; private set; }
        private Dictionary<string, string> data_Attributes;//extra data on item like it's name ....
        /// <summary>
        /// the users who rated this item and the rating they have given to it.
        /// </summary>
        private Dictionary<User<R>, R> votes;
     
        public Item(string itemId)
        {
            itemID = itemId;
            StatisticsAttributes = new StatisticAttributes();
            data_Attributes = new Dictionary<string, string>();
            votes = new Dictionary<User<R>, R>();
        }

        /// <summary>
        /// get item ID
        /// </summary>
        public string ItemID
        {
            get { return itemID; }
        }


        /// <summary>
        /// get all data attributes i.e.:  movie name and generes
        /// </summary>
        public Dictionary<string, string> Data_Attributes
        {
            get { return data_Attributes; }
        }

        /// <summary>
        /// get Users and scores for the item
        /// </summary>
        public Dictionary<User<R>, R> Scores 
        {
            get { return votes; } 
        }

        /// <summary>
        /// set non numerical data on item 
        /// </summary>
        /// <param name="atr">Name of data</param>
        /// <param name="value">Value of data</param>
        public void set_data_Attribute(string atr, string value)
        {
            if (data_Attributes.ContainsKey(atr))
            {
                data_Attributes[atr] = value;
            }
            else
            {
                data_Attributes.Add(atr, value);
            }
        }


        /// <summary>
        /// returns the rating given to this item by user. in case of non rated item -returns null.
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public R getRating(User<R> user)
        {
            if (user == null || !votes.ContainsKey(user))
                return new R();

            return votes[user];
        }

        /// <summary>
        /// add score to scores of user for item
        /// </summary>
        /// <param name="item"></param>
        /// <param name="score"></param>
        public void AddScore(User<R> user, R score)
        {
            if (votes.ContainsKey(user))
            {
                votes[user] = score;
            }
            else
            {
                votes.Add(user, score);
            }
        }

        /// <summary>
        /// returns true if user has rated the item.
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool hasRating(User<R> user)
        {
            return this.Scores.ContainsKey(user);
        }

        #region IComparer Members
        public class CompareByAVG : IComparer<Item<R>>
        {


            #region IComparer<Item<R>> Members

            public int Compare(Item<R> x, Item<R> y)
            {
                double itemx = x.StatisticsAttributes.AverageRating;
                double itemy = y.StatisticsAttributes.AverageRating;
                return itemx==itemy?1:0;
            }

            #endregion
        }
        /*
        public class CompareByPrediction : IComparer<Item<R>>
        {

            #region IComparer<Item<R>> Members

            public int Compare(Item<R> x, Item<R> y)
            {
                double itemx = x.StatisticsAttributes.Prediction;
                double itemy = y.StatisticsAttributes.Prediction;
                return itemx==itemy?1:0;
            }

            #endregion
        }
        */
        /// <summary>
        /// Compare to items by ID , if ID is a number then will convert to number and compares the Ids as numbers
        /// But if ID is string or too long for int64 it will compare the IDs like strings
        /// this comparer shold be used for IDs that there are numbers only
        /// </summary>
        public class CompareByID : IComparer<Item<R>>
        {
            #region IComparer<Item> Members

            public int Compare(Item<R> x, Item<R> y)
            {
                string itemx = x.ItemID;
                string itemy = y.ItemID;
                Int64 Ix, Iy;
                bool resX = Int64.TryParse(itemx, NumberStyles.Integer,
                                        CultureInfo.InvariantCulture, out Ix);
                bool resY = Int64.TryParse(itemx, NumberStyles.Integer,
                                        CultureInfo.InvariantCulture, out Iy);
                if (resX && resY)
                {
                    try
                    {
                        return Ix.CompareTo(Iy);
                    }
                    catch (Exception)
                    {

                        throw;
                    }


                }
                return itemx.CompareTo(itemy);
            }

            #endregion
        }

        #endregion

        class UseEqualityComparer : IEqualityComparer<Item<R>>
        {
            #region IEqualityComparer<Item<R>> Members

            public bool Equals(Item<R> x, Item<R> y)
            {
                return x.itemID.Equals(y.itemID);
            }

            public int GetHashCode(Item<R> obj)
            {
                throw new NotImplementedException();
            }

            #endregion
        }


    }
}
